/***************************************************************************\
 * The Mattise Kernel: Userland Software									*
 * Copyright 2007 Matthew Iselin											*
 * Licensed under the GPL													*
 *																			*
 * main.c																	*
 *																			*
 * Entrypoint for the FAT driver											*
 *																			*
\***************************************************************************/

/***************************************************************************
 * Includes
 ***************************************************************************/
#include <stdio.h>
#include <fcntl.h>
#include <mmsg.h>
#include <process.h>
#include <vfs.h>
#include "fat.h"

/***************************************************************************
 * Functions
 ***************************************************************************/

// Checks if the number is a power of 2
static int IsPowerOf2(unsigned arg)
{
	unsigned log;

	for(log = 0; log < 16; log++)
	{
		if(arg & 1)
		{
			arg >>= 1;
			return (arg != 0) ? -1 : log;
		}
		arg >>= 1;
	}
	return -1;
}

// Checks for a FAT bootsector
int NotFatBootsector( unsigned char *buf )
{
	// temporary storage for bytes from the BPB
	unsigned temp;
	
	// return value
	int bad = 0;

	// is the first set of bytes valid? (must be a jmp/nop)
	if( ! ( ( buf[0] == 0xE9 ) || ( buf[0] == 0xEB && buf[2] == 0x90 ) ) )
	{
		bad = 1; // not there
	}
	
	// check that sectors per cluster is a power of 2
	temp = buf[13];
	if( IsPowerOf2( temp ) < 0 )
	{
		bad = 1; // nope
	}
	
	// how many FATs - must be at least 1, maximum of 2
	temp = buf[16];
	if( temp != 1 && temp != 2 )
	{
		bad = 1;
	}
	
	// return to the caller
	return bad;
}

// FAT filesystem handler
void fat_handler()
{
	// message size and message buffer
	int msize;
	char* mbuff;
	
	// already, we will have a message waiting (INITTHRD)
	struct RecvMsgData myMsg;
	while( !mattiseHasMessage() );
	
	// allocate a buffer for the message
	msize = mattiseGetMessageSize();
	mbuff = mattiseAllocMessage();
	
	// get the message
	mattiseRecvMessage( mbuff, msize + 1, &myMsg );
	
	// open the message, get the data
	struct vfs_ReqData* req = (struct vfs_ReqData*) mbuff;
	
	// take in the info
	int drvid = req->reqArg1;
	int myprocess = req->reqArg2;
	char* fn = (char*) malloc( req->datlen + 1 );
	strncpy( fn, req->data, req->datlen );
	
	// free the memory
	mattiseFreeMessage( mbuff );
	
	// open the device
	char tmp[32],sec[512]; sprintf( tmp, "/dev/dr%d", drvid );
	int devdesc = open( tmp, 0, O_RDWR );
	
	// read in the BPB
	lseek( devdesc, 0, SEEK_SET );	
	read( devdesc, sec, 1 );
	
	// fill it in
	struct BPB_FAT* myBPB = (struct BPB_FAT*) sec;
	struct BPB_FAT32* myBPB32 = (struct BPB_FAT32*) (sec + sizeof( struct BPB_FAT )); // TODO: check what version of FAT we're using,
																					// at the moment only FAT32 is supported
	
	
	// our file descriptor
	fat32_filedesc myFile;
	myFile.dev = drvid;
	myFile.devdesc = devdesc;
	
	// get the start of the data area
	if( myBPB->BPB_BytsPerSec )
		myFile.RootDirCount = ( myBPB->BPB_RootEntCnt * 32 ) + ( myBPB->BPB_BytsPerSec - 1 ) / myBPB->BPB_BytsPerSec;
	else
	{
		// because it's fatal and may reach into the other FAT stuff, tell the user
		printf( "FATAL ERROR: A FAT HANDLER (%d) has crashed!\n", getpid() );
		close( devdesc );
		exit( 0 ); // this is fatal
	}
	myFile.DataStart = myBPB->BPB_RsvdSecCnt + ( myBPB->BPB_NumFATs * myBPB32->BPB_FATSz32 ) + myFile.RootDirCount;
		
	// open the file
	int success = fat32_open( &myFile, *myBPB, *myBPB32, fn );
	
	// send back the descriptor now that we are ready
	req->descriptor = success == -1 ? -1 : getpid();
	mattiseSendMessage( myprocess, VFS_REPMSG, (char*) req, sizeof(  struct vfs_ReqData ) );
	
	// internal address
	unsigned int myInternalOffset = 0;
	
	// and now enter our message loop
	int running = 1;
	int i;
	while( running )
	{
		// do we have a message?
		if( mattiseHasMessage() )
		{
			// allocate a buffer for the message
			msize = mattiseGetMessageSize();
			mbuff = mattiseAllocMessage();
			
			// verify the buffer
			if( !mbuff )
				continue;
			
			// get the message
			mattiseRecvMessage( mbuff, msize + 1, &myMsg );
			
			// get the message
			struct vfs_ReqData* req = (struct vfs_ReqData*) mbuff;
			
			// what was it?
			switch( myMsg.msgcode )
			{			
				// read: send back some data
				case VFS_READ:
				
					// read it in
					req->datlen = fat32_read( &myFile, *myBPB, *myBPB32, myInternalOffset, req->datlen, req->data );
					mattiseSendMessage( req->srcpid, VFS_READ, (char*) req, sizeof( struct vfs_ReqData ) );

					break;
					
				// write: write some data
				case VFS_WRITE:
				
					// write it
					req->datlen = fat32_write( &myFile, *myBPB, *myBPB32, myInternalOffset, req->datlen, req->data );
					mattiseSendMessage( req->srcpid, VFS_WRITE, (char*) req, sizeof( struct vfs_ReqData ) );

					break;
				
				// seek: seek to a new location
				case VFS_SEEK:
				
					// where do we seek to?
					switch( req->reqArg2 )
					{
						case SEEK_SET:
						
							// from the start
							myInternalOffset = req->reqArg1;
							
							break;
							
						case SEEK_CUR:
						
							// from now
							myInternalOffset += req->reqArg1;
					};
					
					// return new position
					req->reqArg1 = myInternalOffset;
					mattiseSendMessage( req->srcpid, VFS_SEEK, (char*) req, sizeof( struct vfs_ReqData ) );
					
					break;
				
				// stat: give some information about the device
				case VFS_STAT:
				
					{
					
					// the return structure
					struct stat sret;
					
					// fill in the device id
					sret.st_dev = getpid();
					sret.st_mode = S_IFBLK;
					sret.st_size = sret.st_blocks = myFile.myDirEnt.DIR_FileSize;
					sret.st_blksize = 1;
					
					// send it off
					memcpy( req->data, (char*) &sret, sizeof( struct stat ) );
					mattiseSendMessage( req->srcpid, VFS_STAT, (char*) req, sizeof( struct vfs_ReqData ) );
					
					}
					break;
				
				// getc/putc don't do anything
				case VFS_GETC:
				case VFS_PUTC:

					break;
				
				// close: just finish
				case VFS_CLOSE:
				
					// all done
					running = 0;
					
					break;
			}
			
			// free the memory
			mattiseFreeMessage( mbuff );
		}
	}
	// loop forever
	while( 1 );
}

// A FAT filesystem
void fat_fs()
{
	// message size and message buffer
	int msize;
	char* mbuff;
	
	// already, we will have a message waiting (INITTHRD)
	struct RecvMsgData myMsg;
	while( !mattiseHasMessage() );
	
	// allocate a buffer for the message
	msize = mattiseGetMessageSize();
	mbuff = mattiseAllocMessage();
	
	// get the message
	mattiseRecvMessage( mbuff, msize + 1, &myMsg );
	
	// open the message, get the data
	struct vfs_ReqData* req = (struct vfs_ReqData*) mbuff;

	// take in the info
	int drvid = req->reqArg1;
	
	// free the memory
	mattiseFreeMessage( mbuff );
	
	// mount this drive in the devfs
	struct vfs_Action mountReq;	
	mountReq.vfsArg3 = getpid();
	mountReq.nmlen = sprintf( mountReq.filenm, "dr%d/", drvid );
	mountReq.msgcode = VFS_MOUNT;
	
	// send off the message
	mattiseSendMessage(	1, /* todo: find the vfs in a different way */
						VFS_MOUNT,
						(char*) &mountReq,
						sizeof( struct vfs_Action )
						);
	
	// and now enter our message loop
	int running = 1;
	int i;
	while( running )
	{
		// do we have a message?
		if( mattiseHasMessage() )
		{
			// allocate a buffer for the message
			msize = mattiseGetMessageSize();
			mbuff = mattiseAllocMessage();
			
			// verify the buffer
			if( !mbuff )
				continue;
			
			// get the message
			mattiseRecvMessage( mbuff, msize + 1, &myMsg );
			
			// get the message
			struct vfs_ReqData* req = (struct vfs_ReqData*) mbuff;
			struct vfs_ReqData* rep = (struct vfs_ReqData*) malloc( sizeof( struct vfs_ReqData ) );
			
			// what was it?
			switch( myMsg.msgcode )
			{
				// open: send back a descriptor
				case VFS_OPEN:
					
					{
						// create a new thread
						int desc = createthread( (int) fat_handler, 2, "fat handler thread", 10 );
						
						// also send the init packet to the new thread
						rep->reqArg1 = drvid;
						rep->reqArg2 = req->srcpid;
						rep->datlen = sprintf( rep->data, "%s", req->data );
						mattiseSendMessage( desc, VFS_REPMSG, (char*) rep, sizeof( struct vfs_ReqData ) );
					}
					break;
			}
			
			// free the memory
			free( rep );
			mattiseFreeMessage( mbuff );
		}
	}
	// loop forever
	while( 1 );
}

// Initializes FAT for the system
void InitFat()
{
	// sector buffer
	uchar_t* sec = (uchar_t*) malloc( 512 );
	memset( sec, 0, 512 );

	// find all the drives we can
	int i = 0, dev = 0;
	char tmp[32];
	while( dev != -1 && i < 32 )
	{		
		// test each drive possible
		sprintf( tmp, "/dev/dr%d", i );
		
		// open it up
		dev = open( tmp, 0, O_RDWR );
		
		// is it valid?
		if( dev != -1 )
		{
			// read in the first sector
			lseek( dev, 0, SEEK_SET );
			read( dev, sec, 1 );
			
			// is it a valid FAT bootsector?
			if( NotFatBootsector( sec ) )
			{
				// close the device
				close( dev );
				
				// keep going
				i++;
				continue;
			}
			else
			{				
				// close it
				close( dev );
			}
		}
		
		// increment the device id
		i++;
	}
	free( sec );
}

// Entry point
int main( int argc, char* argv[] )
{	
	// initialize it
	InitFat();
	
	// loop forever
	while( 1 );
	
	// return success
	return 0;
}
